import ustruct as struct

from tft import font_gb_16x16 as font_gb
from tft import vga1_16x32 as font16
from tft import vga1_8x16 as font8

from tft import GBfont


GB16 = GBfont.gb2312(16)  # UNICODE字体
GB24 = GBfont.gb2312(24)  # UNICODE字体
GB32 = GBfont.gb2312(32)  # UNICODE字体
# GB32 = font_gb.Font32(32)  # 自定义图形字体
GB48 = font_gb.Font48(48)  # 自定义图形字体

_BIT7 = const(0x80)
_BIT6 = const(0x40)
_BIT5 = const(0x20)
_BIT4 = const(0x10)
_BIT3 = const(0x08)
_BIT2 = const(0x04)
_BIT1 = const(0x02)
_BIT0 = const(0x01)
_BIT = [_BIT0, _BIT1, _BIT2, _BIT3, _BIT4, _BIT5, _BIT6, _BIT7]

_ENCODE_PIXEL = ">H"
_ENCODE_POS = ">HH"
_DECODE_PIXEL = ">BBB"

def encode_pos(x, y):
    """将位置编码为字节。

    该函数接收两个坐标 x 和 y，使用 struct 模块将它们编码为字节格式。
    编码的目的是将位置信息转换为易于存储或传输的格式。

    参数:
    x (int/float): 位置的 x 坐标。
    y (int/float): 位置的 y 坐标。

    返回值:
    bytes: 编码后的位置的字节表示。
    """
    return struct.pack(_ENCODE_POS, x, y)


def encode_pixel(color):
    """
    Encode a pixel color into bytes.

    This function takes a color value and encodes it into a byte format according to a predefined structure.
    It is primarily used for converting color information into a format that can be stored or transmitted.

    Parameters:
    - color: The color value to be encoded, usually an integer or a color represented in a specific format.

    Returns:
    - bytes: The encoded color information in byte format.
    """
    return struct.pack(_ENCODE_PIXEL, color)

def color565(red, green=0, blue=0):
    """
    将红、绿、蓝分量值（0-255）转换为16位565编码。

    参数:
    red (int 或 tuple/list): 红分量值或包含 (red, green, blue) 值的元组/列表。
    green (int): 绿分量值。默认为0。
    blue (int): 蓝分量值。默认为0。

    返回:
    int: 16位565编码的颜色值。
    """
    try:
        # 如果 red 是一个元组或列表，解包前三个元素到 red, green, 和 blue
        if isinstance(red, (tuple, list)):
            if len(red) < 3 or not all(isinstance(x, int) for x in red[:3]):
                raise ValueError("输入的元组或列表必须至少包含三个整数")
            red, green, blue = red[:3]

        # 验证输入值是否在 0-255 范围内
        if not (0 <= red <= 255 and 0 <= green <= 255 and 0 <= blue <= 255):
            raise ValueError("RGB 分量值必须在 0-255 范围内")

        # 将 RGB 值转换为 565 编码
        return (red & 0xF8) << 8 | (green & 0xFC) << 3 | blue >> 3

    except ValueError as e:
        print(f"错误: {e}")
        return None




class ImageDisplayer:

    def __init__(self, tft_display):
        self.tft_display = tft_display

    def read_bmp_header(self, file):
        """
        读取 BMP 文件头信息。

        参数:
            file (file): 打开的 BMP 文件对象。

        返回:
            tuple: 包含偏移量、宽度、高度和深度的元组。
        """
        if file.read(2) != b'BM':
            raise ValueError("不是 BMP 文件")
        file.read(8)  # 文件大小(4字节), 创建者字节(4字节)
        offset = int.from_bytes(file.read(4), 'little')
        file.read(4)  # DIB 头大小
        width = int.from_bytes(file.read(4), 'little')
        height = int.from_bytes(file.read(4), 'little')
        if int.from_bytes(file.read(2), 'little') != 1:  # 平面数必须为1
            raise ValueError("平面数必须为1")
        depth = int.from_bytes(file.read(2), 'little')
        if depth != 24 or int.from_bytes(file.read(4), 'little') != 0:  # 压缩方法必须为无压缩
            raise ValueError("不支持的 BMP 格式")
        return offset, width, height

    def display_bmp(self, file_path, x, y):
        """
        在 TFT 显示屏上显示 BMP 图像。

        参数:
            file_path (str): BMP 文件的路径。
            x (int): 图像显示的起始 x 坐标。
            y (int): 图像显示的起始 y 坐标。
        """
        with open(file_path, 'rb') as f:
            offset, width, height = self.read_bmp_header(f)
            rowsize = (width * 3 + 3) & ~3
            flip = height > 0
            if height < 0:
                height = -height
            w, h = width, height
            self.tft_display._set_window(x, y, x + w - 1, y + h - 1)

            buffer = bytearray(rowsize)
            for row in range(h):
                if flip:
                    pos = offset + (height - 1 - row) * rowsize
                else:
                    pos = offset + row * rowsize
                if f.tell() != pos:
                    f.seek(pos)
                f.readinto(buffer)
                pixels = bytearray(w * 2)  # 每个像素2字节
                for col in range(w):
                    bgr = buffer[col * 3:col * 3 + 3]
                    color = color565(bgr[2], bgr[1], bgr[0])
                    pixels[col * 2:(col * 2 + 2)] = struct.pack('>H', color)
                self.tft_display.blit_buffer(pixels, x, y + row, w, 1)


class TextDisplayer:

    def __init__(self, tft_display):
        self.tft_display = tft_display

    def text8(self, font, text, x0, y0, color, background):
        """
        Internal method to write characters with width of 8 and
        heights of 8 or 16.

        Args:
            font (module): font module to use
            text (str): text to write
            x0 (int): column to start drawing at
            y0 (int): row to start drawing at
            color (int): 565 encoded color to use for characters
            background (int): 565 encoded color to use for background
        """
        for char in text:
            ch = ord(char)
            if (
                    font.FIRST <= ch < font.LAST and x0 + font.WIDTH <= self.tft_display.width and y0 + font.HEIGHT <= self.tft_display.height):

                if font.HEIGHT == 8:
                    passes = 1
                    size = 8
                    each = 0
                else:
                    passes = 2
                    size = 16
                    each = 8

                for line in range(passes):
                    idx = (ch - font.FIRST) * size + (each * line)
                    buffer = struct.pack('>64H', color if font.FONT[idx] & _BIT7 else background,
                        color if font.FONT[idx] & _BIT6 else background,
                        color if font.FONT[idx] & _BIT5 else background,
                        color if font.FONT[idx] & _BIT4 else background,
                        color if font.FONT[idx] & _BIT3 else background,
                        color if font.FONT[idx] & _BIT2 else background,
                        color if font.FONT[idx] & _BIT1 else background,
                        color if font.FONT[idx] & _BIT0 else background,
                        color if font.FONT[idx + 1] & _BIT7 else background,
                        color if font.FONT[idx + 1] & _BIT6 else background,
                        color if font.FONT[idx + 1] & _BIT5 else background,
                        color if font.FONT[idx + 1] & _BIT4 else background,
                        color if font.FONT[idx + 1] & _BIT3 else background,
                        color if font.FONT[idx + 1] & _BIT2 else background,
                        color if font.FONT[idx + 1] & _BIT1 else background,
                        color if font.FONT[idx + 1] & _BIT0 else background,
                        color if font.FONT[idx + 2] & _BIT7 else background,
                        color if font.FONT[idx + 2] & _BIT6 else background,
                        color if font.FONT[idx + 2] & _BIT5 else background,
                        color if font.FONT[idx + 2] & _BIT4 else background,
                        color if font.FONT[idx + 2] & _BIT3 else background,
                        color if font.FONT[idx + 2] & _BIT2 else background,
                        color if font.FONT[idx + 2] & _BIT1 else background,
                        color if font.FONT[idx + 2] & _BIT0 else background,
                        color if font.FONT[idx + 3] & _BIT7 else background,
                        color if font.FONT[idx + 3] & _BIT6 else background,
                        color if font.FONT[idx + 3] & _BIT5 else background,
                        color if font.FONT[idx + 3] & _BIT4 else background,
                        color if font.FONT[idx + 3] & _BIT3 else background,
                        color if font.FONT[idx + 3] & _BIT2 else background,
                        color if font.FONT[idx + 3] & _BIT1 else background,
                        color if font.FONT[idx + 3] & _BIT0 else background,
                        color if font.FONT[idx + 4] & _BIT7 else background,
                        color if font.FONT[idx + 4] & _BIT6 else background,
                        color if font.FONT[idx + 4] & _BIT5 else background,
                        color if font.FONT[idx + 4] & _BIT4 else background,
                        color if font.FONT[idx + 4] & _BIT3 else background,
                        color if font.FONT[idx + 4] & _BIT2 else background,
                        color if font.FONT[idx + 4] & _BIT1 else background,
                        color if font.FONT[idx + 4] & _BIT0 else background,
                        color if font.FONT[idx + 5] & _BIT7 else background,
                        color if font.FONT[idx + 5] & _BIT6 else background,
                        color if font.FONT[idx + 5] & _BIT5 else background,
                        color if font.FONT[idx + 5] & _BIT4 else background,
                        color if font.FONT[idx + 5] & _BIT3 else background,
                        color if font.FONT[idx + 5] & _BIT2 else background,
                        color if font.FONT[idx + 5] & _BIT1 else background,
                        color if font.FONT[idx + 5] & _BIT0 else background,
                        color if font.FONT[idx + 6] & _BIT7 else background,
                        color if font.FONT[idx + 6] & _BIT6 else background,
                        color if font.FONT[idx + 6] & _BIT5 else background,
                        color if font.FONT[idx + 6] & _BIT4 else background,
                        color if font.FONT[idx + 6] & _BIT3 else background,
                        color if font.FONT[idx + 6] & _BIT2 else background,
                        color if font.FONT[idx + 6] & _BIT1 else background,
                        color if font.FONT[idx + 6] & _BIT0 else background,
                        color if font.FONT[idx + 7] & _BIT7 else background,
                        color if font.FONT[idx + 7] & _BIT6 else background,
                        color if font.FONT[idx + 7] & _BIT5 else background,
                        color if font.FONT[idx + 7] & _BIT4 else background,
                        color if font.FONT[idx + 7] & _BIT3 else background,
                        color if font.FONT[idx + 7] & _BIT2 else background,
                        color if font.FONT[idx + 7] & _BIT1 else background,
                        color if font.FONT[idx + 7] & _BIT0 else background)
                    self.tft_display.blit_buffer(buffer, x0, y0 + 8 * line, 8, 8)

                x0 += 8

    def text16(self, font, text, x0, y0, color, background):
        """
        Internal method to draw characters with width of 16 and heights of 16
        or 32.

        Args:
            font (module): font module to use
            text (str): text to write
            x0 (int): column to start drawing at
            y0 (int): row to start drawing at
            color (int): 565 encoded color to use for characters
            background (int): 565 encoded color to use for background
        """
        for char in text:
            ch = ord(char)
            if (
                    font.FIRST <= ch < font.LAST and x0 + font.WIDTH <= self.tft_display.width and y0 + font.HEIGHT <= self.tft_display.height):

                each = 16
                if font.HEIGHT == 16:
                    passes = 2
                    size = 32
                else:
                    passes = 4
                    size = 64

                for line in range(passes):
                    idx = (ch - font.FIRST) * size + (each * line)
                    buffer = struct.pack('>128H', color if font.FONT[idx] & _BIT7 else background,
                        color if font.FONT[idx] & _BIT6 else background,
                        color if font.FONT[idx] & _BIT5 else background,
                        color if font.FONT[idx] & _BIT4 else background,
                        color if font.FONT[idx] & _BIT3 else background,
                        color if font.FONT[idx] & _BIT2 else background,
                        color if font.FONT[idx] & _BIT1 else background,
                        color if font.FONT[idx] & _BIT0 else background,
                        color if font.FONT[idx + 1] & _BIT7 else background,
                        color if font.FONT[idx + 1] & _BIT6 else background,
                        color if font.FONT[idx + 1] & _BIT5 else background,
                        color if font.FONT[idx + 1] & _BIT4 else background,
                        color if font.FONT[idx + 1] & _BIT3 else background,
                        color if font.FONT[idx + 1] & _BIT2 else background,
                        color if font.FONT[idx + 1] & _BIT1 else background,
                        color if font.FONT[idx + 1] & _BIT0 else background,
                        color if font.FONT[idx + 2] & _BIT7 else background,
                        color if font.FONT[idx + 2] & _BIT6 else background,
                        color if font.FONT[idx + 2] & _BIT5 else background,
                        color if font.FONT[idx + 2] & _BIT4 else background,
                        color if font.FONT[idx + 2] & _BIT3 else background,
                        color if font.FONT[idx + 2] & _BIT2 else background,
                        color if font.FONT[idx + 2] & _BIT1 else background,
                        color if font.FONT[idx + 2] & _BIT0 else background,
                        color if font.FONT[idx + 3] & _BIT7 else background,
                        color if font.FONT[idx + 3] & _BIT6 else background,
                        color if font.FONT[idx + 3] & _BIT5 else background,
                        color if font.FONT[idx + 3] & _BIT4 else background,
                        color if font.FONT[idx + 3] & _BIT3 else background,
                        color if font.FONT[idx + 3] & _BIT2 else background,
                        color if font.FONT[idx + 3] & _BIT1 else background,
                        color if font.FONT[idx + 3] & _BIT0 else background,
                        color if font.FONT[idx + 4] & _BIT7 else background,
                        color if font.FONT[idx + 4] & _BIT6 else background,
                        color if font.FONT[idx + 4] & _BIT5 else background,
                        color if font.FONT[idx + 4] & _BIT4 else background,
                        color if font.FONT[idx + 4] & _BIT3 else background,
                        color if font.FONT[idx + 4] & _BIT2 else background,
                        color if font.FONT[idx + 4] & _BIT1 else background,
                        color if font.FONT[idx + 4] & _BIT0 else background,
                        color if font.FONT[idx + 5] & _BIT7 else background,
                        color if font.FONT[idx + 5] & _BIT6 else background,
                        color if font.FONT[idx + 5] & _BIT5 else background,
                        color if font.FONT[idx + 5] & _BIT4 else background,
                        color if font.FONT[idx + 5] & _BIT3 else background,
                        color if font.FONT[idx + 5] & _BIT2 else background,
                        color if font.FONT[idx + 5] & _BIT1 else background,
                        color if font.FONT[idx + 5] & _BIT0 else background,
                        color if font.FONT[idx + 6] & _BIT7 else background,
                        color if font.FONT[idx + 6] & _BIT6 else background,
                        color if font.FONT[idx + 6] & _BIT5 else background,
                        color if font.FONT[idx + 6] & _BIT4 else background,
                        color if font.FONT[idx + 6] & _BIT3 else background,
                        color if font.FONT[idx + 6] & _BIT2 else background,
                        color if font.FONT[idx + 6] & _BIT1 else background,
                        color if font.FONT[idx + 6] & _BIT0 else background,
                        color if font.FONT[idx + 7] & _BIT7 else background,
                        color if font.FONT[idx + 7] & _BIT6 else background,
                        color if font.FONT[idx + 7] & _BIT5 else background,
                        color if font.FONT[idx + 7] & _BIT4 else background,
                        color if font.FONT[idx + 7] & _BIT3 else background,
                        color if font.FONT[idx + 7] & _BIT2 else background,
                        color if font.FONT[idx + 7] & _BIT1 else background,
                        color if font.FONT[idx + 7] & _BIT0 else background,
                        color if font.FONT[idx + 8] & _BIT7 else background,
                        color if font.FONT[idx + 8] & _BIT6 else background,
                        color if font.FONT[idx + 8] & _BIT5 else background,
                        color if font.FONT[idx + 8] & _BIT4 else background,
                        color if font.FONT[idx + 8] & _BIT3 else background,
                        color if font.FONT[idx + 8] & _BIT2 else background,
                        color if font.FONT[idx + 8] & _BIT1 else background,
                        color if font.FONT[idx + 8] & _BIT0 else background,
                        color if font.FONT[idx + 9] & _BIT7 else background,
                        color if font.FONT[idx + 9] & _BIT6 else background,
                        color if font.FONT[idx + 9] & _BIT5 else background,
                        color if font.FONT[idx + 9] & _BIT4 else background,
                        color if font.FONT[idx + 9] & _BIT3 else background,
                        color if font.FONT[idx + 9] & _BIT2 else background,
                        color if font.FONT[idx + 9] & _BIT1 else background,
                        color if font.FONT[idx + 9] & _BIT0 else background,
                        color if font.FONT[idx + 10] & _BIT7 else background,
                        color if font.FONT[idx + 10] & _BIT6 else background,
                        color if font.FONT[idx + 10] & _BIT5 else background,
                        color if font.FONT[idx + 10] & _BIT4 else background,
                        color if font.FONT[idx + 10] & _BIT3 else background,
                        color if font.FONT[idx + 10] & _BIT2 else background,
                        color if font.FONT[idx + 10] & _BIT1 else background,
                        color if font.FONT[idx + 10] & _BIT0 else background,
                        color if font.FONT[idx + 11] & _BIT7 else background,
                        color if font.FONT[idx + 11] & _BIT6 else background,
                        color if font.FONT[idx + 11] & _BIT5 else background,
                        color if font.FONT[idx + 11] & _BIT4 else background,
                        color if font.FONT[idx + 11] & _BIT3 else background,
                        color if font.FONT[idx + 11] & _BIT2 else background,
                        color if font.FONT[idx + 11] & _BIT1 else background,
                        color if font.FONT[idx + 11] & _BIT0 else background,
                        color if font.FONT[idx + 12] & _BIT7 else background,
                        color if font.FONT[idx + 12] & _BIT6 else background,
                        color if font.FONT[idx + 12] & _BIT5 else background,
                        color if font.FONT[idx + 12] & _BIT4 else background,
                        color if font.FONT[idx + 12] & _BIT3 else background,
                        color if font.FONT[idx + 12] & _BIT2 else background,
                        color if font.FONT[idx + 12] & _BIT1 else background,
                        color if font.FONT[idx + 12] & _BIT0 else background,
                        color if font.FONT[idx + 13] & _BIT7 else background,
                        color if font.FONT[idx + 13] & _BIT6 else background,
                        color if font.FONT[idx + 13] & _BIT5 else background,
                        color if font.FONT[idx + 13] & _BIT4 else background,
                        color if font.FONT[idx + 13] & _BIT3 else background,
                        color if font.FONT[idx + 13] & _BIT2 else background,
                        color if font.FONT[idx + 13] & _BIT1 else background,
                        color if font.FONT[idx + 13] & _BIT0 else background,
                        color if font.FONT[idx + 14] & _BIT7 else background,
                        color if font.FONT[idx + 14] & _BIT6 else background,
                        color if font.FONT[idx + 14] & _BIT5 else background,
                        color if font.FONT[idx + 14] & _BIT4 else background,
                        color if font.FONT[idx + 14] & _BIT3 else background,
                        color if font.FONT[idx + 14] & _BIT2 else background,
                        color if font.FONT[idx + 14] & _BIT1 else background,
                        color if font.FONT[idx + 14] & _BIT0 else background,
                        color if font.FONT[idx + 15] & _BIT7 else background,
                        color if font.FONT[idx + 15] & _BIT6 else background,
                        color if font.FONT[idx + 15] & _BIT5 else background,
                        color if font.FONT[idx + 15] & _BIT4 else background,
                        color if font.FONT[idx + 15] & _BIT3 else background,
                        color if font.FONT[idx + 15] & _BIT2 else background,
                        color if font.FONT[idx + 15] & _BIT1 else background,
                        color if font.FONT[idx + 15] & _BIT0 else background)
                    self.tft_display.blit_buffer(buffer, x0, y0 + 8 * line, 16, 8)
            x0 += font.WIDTH

    def text_gb(self, font, size, text, x0, y0, color, background):
        """
        显示中文
        font (class): 字体
        size (int): 字体大小
        text (str): 要显示的中文文本（不能含有半角英文符号）
        x0 (int): column to start drawing at
        y0 (int): row to start drawing at
        color (int): 565 encoded color to use for background
        background (int): 565 encoded color to use for background
        """
        # 使用大端序 '>H' 确保字节序一致
        color_bytes = struct.pack('>H', color)
        background_bytes = struct.pack('>H', background)

        buffer = bytearray(font.WIDTH * font.HEIGHT * 2)  # 预分配整个字符的缓冲区

        for char in text:
            if (x0 + font.WIDTH <= self.tft_display.width and y0 + font.HEIGHT <= self.tft_display.height):
                buffer_index = 0

                for line in range(2):  # 分两次显示，先显示上半边后显示下半边
                    idx = line * (font.SIZE // 2)
                    for x in range(font.SIZE // 2):
                        byte_value = font.FONT[char][idx + x]
                        for i in range(8):
                            if byte_value & _BIT[7 - i]:
                                buffer[buffer_index:buffer_index + 2] = color_bytes
                            else:
                                buffer[buffer_index:buffer_index + 2] = background_bytes
                            buffer_index += 2

                # 使用 blit_buffer 一次性传输整个字符的数据
                self.tft_display.blit_buffer(buffer, x0, y0, font.WIDTH, font.HEIGHT)
                x0 += font.WIDTH  # 显示下一个字的时候x坐标增加字体宽度



    def text(self, size, text, x0, y0, color, background):
        """
        Draw text on display in specified font and colors. 8 and 16 bit wide
        fonts are supported.

        Args:
            size : 中文支持16,24,32,48；英文默认使用16号字体，支持8、16种字号.
            text (str): text to write
            x0 (int): column to start drawing at
            y0 (int): row to start drawing at
            color (int): 565 encoded color to use for characters
            background (int): 565 encoded color to use for background
        """
        if not size in [8, 16, 24, 32, 48]:
            print(f"不支持{size}号大小的字体。")
            size = 16

        current_x = x0
        i = 0
        while i < len(text):
            char = text[i]

            # 英文字母只有8号和16号字体，默认使用16号字体，不带粗体，如果需要粗体，
            # 请修改代码导入的字库，从romfonts文件夹中复制字库文件

            if len(char.encode('utf-8')) == 1 and size <= 32:  # ASCII
                if size == 8:
                    self.text8(font8, char, current_x, y0, color, background)
                    current_x += 8
                else:
                    # 英文只支持2种大小的字体，默认使用16号字体
                    self.text16(font16, char, current_x, y0, color, background)
                    current_x += 16

            # 汉字只有16,24,32号字体，包含完整的汉字字库
            # 48号字体为自定义的字体，随机附带的字库定义了时钟显示需要的几个数字和符号
            else:  # UNICODE
                if size == 16:
                    font_gb = GB16.str(char)  # 使用字体文件
                elif size == 24:
                    font_gb = GB24.str(char)  # 使用字体文件
                elif size == 32:
                    # font_gb = GB32
                    font_gb = GB32.str(char)  # 使用字体文件
                elif size == 48:
                    font_gb = GB48
                self.text_gb(font_gb, size, char, current_x, y0, color, background)
                current_x += font_gb.WIDTH  # 假设font.WIDTH是全角字符的宽度
            i += 1
